#include "operator.h"
#include "function.h"
#include "errorlog.h"
#include "parameterlist.h"
#include "type.h"

Type UnaryOperator::type()
{
    return Type(Type::Scalar);
}

void UnaryOperator::resolveAll()
{
    this->Operator::resolveAll();

    if ( !m_arg->type().isScalar() )
        ErrorLog::error( location(), "The argument to the operator must be a scalar");
}

Type BinaryOperator::type()
{
    switch( operatorKind() )
    {
    case Operator::Filter:
        return Type(Type::List);
    default:
        if ( m_arg1->type().isList() || m_arg2->type().isList() )
            return Type(Type::List);
        return Type(Type::Scalar);
    }
    return Type(Type::Scalar);
}

void BinaryOperator::resolveAll()
{
    this->Operator::resolveAll();

    switch( operatorKind() )
    {
    case Filter:
        if ( !m_arg1->type().isList() )
            ErrorLog::error( location(), "The filter operator can only be applied to lists");
        if ( !m_arg2->type().isScalar() )
            ErrorLog::error( location(), "The lambda function passed to the filter must always be of scalar type");
    case Index:
        if ( !m_arg1->type().isList() )
            ErrorLog::error( location(), "The index operator can only be applied to lists");
        if ( !m_arg2->type().isScalar() )
            ErrorLog::error( location(), "The index must always be a scalar");
        break;
    default:
        // TODO: What about list operations?
//        if ( m_arg1->type().kind() != m_arg2->type().kind() )
//            ErrorLog::error( location(), "The types of the two arguments do not match");
        break;
    }
}

void CallOperator::resolveAll()
{
    this->Operator::resolveAll();

    Node* n = this->nspace()->resolve(this->m_identifier);
    if ( !n )
    {
        ErrorLog::error( location(), "Unknown function " + this->m_identifier );
        return;
    }
    m_resolvedFunction = dynamic_cast<Function*>(n);
    if ( !m_resolvedFunction )
        ErrorLog::error( location(), m_identifier + " is not a function" );

    // Does the number of parameters match?
    bool additionalParameter = 0;
    if ( dynamic_cast<PathOperator*>(this->parentNode()) )
    {
        PathOperator* op = dynamic_cast<PathOperator*>(this->parentNode());
        if ( op->expressions()[0] != this )
            additionalParameter = 1;
    }
    if ( m_resolvedFunction && m_resolvedFunction->parameterList()->count() != this->m_callArgs->count() + additionalParameter )
        ErrorLog::error( location(), "The function " + m_identifier + " requires a different number of arguments" );
}

Type CallOperator::type()
{
    if ( m_resolvedFunction )
        return m_resolvedFunction->type();
    return Type();
}

Type RangeOperator::type()
{
    return Type(Type::Scalar);
}

void RangeOperator::resolveAll()
{
    this->Operator::resolveAll();

    if ( m_left && !m_left->type().isScalar() )
        ErrorLog::error(m_left->location(), "Arguments to the index operator must always be scalars");
    if ( m_right && !m_right->type().isScalar() )
        ErrorLog::error(m_left->location(), "Arguments to the index operator must always be scalars");
}

PathOperator::PathOperator( Node* firstElement, Node* secondElement, const YYLTYPE&loc )
    : Operator(Operator::Path, loc)
{
    if ( dynamic_cast<PathOperator*>(firstElement) )
    {
        foreach( Node* n, dynamic_cast<PathOperator*>(firstElement)->expressions() )
        {
            appendChild(n);
        }
    }
    else
        appendChild(firstElement);
    appendChild(secondElement);
}

QList<Node*> PathOperator::expressions() const
{
    QList<Node*> result;
    Node* p = this->firstChild();
    while( p )
    {
        result.append(p);
        p = p->nextSibling();
    }

    return result;
}

Type PathOperator::type()
{
    Type t( Type::Scalar );

    Node* p = this->firstChild();
    while( p )
    {
        switch( p->type().kind() )
        {
            case Type::Invalid:
                return Type();
            case Type::Scalar:
                break;
            case Type::List:
                t = Type(Type::List);
                break;
        }
        p = p->nextSibling();
    }
    return t;
}

DotOperator::DotOperator( Node* arg, const QString& identifier, const YYLTYPE& loc )
        : Operator(Operator::Dot, loc), m_arg( arg ), m_identifier(identifier)
{
    appendChild(arg);
}

Type DotOperator::type()
{
    // The property of something is always a scalar in Wala
    return Type(Type::Scalar);
}

void DotCallOperator::resolveAll()
{
    this->Operator::resolveAll();

    if ( !m_object->type().isScalar() )
        ErrorLog::error(m_object->location(), "The left side of the '.' operator must be a scalar type" );
}

Type DotCallOperator::type()
{
    return Type();
}
